/*
 *    Copyright © OpenAtom Foundation.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.inspur.edp.formserver.vmmanager.service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.deser.std.StringDeserializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.node.JsonNodeType;
import com.inspur.edp.bef.bizentity.GspBizEntityElement;
import com.inspur.edp.bef.bizentity.GspBizEntityObject;
import com.inspur.edp.bef.bizentity.GspBusinessEntity;
import com.inspur.edp.bef.bizentity.json.object.BizObjectSerializer;
import com.inspur.edp.cef.designtime.api.IGspCommonDataType;
import com.inspur.edp.cef.designtime.api.IGspCommonField;
import com.inspur.edp.cef.designtime.api.collection.GspAssociationKeyCollection;
import com.inspur.edp.cef.designtime.api.element.GspAssociation;
import com.inspur.edp.cef.designtime.api.element.GspAssociationKey;
import com.inspur.edp.cef.designtime.api.json.element.GspAssoKeyDeserializer;
import com.inspur.edp.das.commonmodel.IGspCommonElement;
import com.inspur.edp.das.commonmodel.IGspCommonObject;
import com.inspur.edp.das.commonmodel.collection.GspElementCollection;
import com.inspur.edp.das.commonmodel.entity.element.GspCommonAssociation;
import com.inspur.edp.das.commonmodel.json.element.CmElementDeserializer;
import com.inspur.edp.das.commonmodel.json.element.GspCommonAssociationDeserializer;
import com.inspur.edp.formserver.viewmodel.GspViewModel;
import com.inspur.edp.formserver.viewmodel.GspViewModelElement;
import com.inspur.edp.formserver.viewmodel.GspViewObject;
import com.inspur.edp.formserver.viewmodel.common.ConvertUtils;
import com.inspur.edp.formserver.viewmodel.common.ViewModelMapping;
import com.inspur.edp.formserver.viewmodel.common.mapping.GspVoElementMapping;
import com.inspur.edp.formserver.viewmodel.common.mapping.GspVoElementSourceType;
import com.inspur.edp.formserver.viewmodel.common.mapping.GspVoObjectSourceType;
import com.inspur.edp.formserver.viewmodel.json.element.ViewElementDeserializer;
import com.inspur.edp.formserver.viewmodel.json.element.ViewElementSerializer;
import com.inspur.edp.formserver.viewmodel.json.mapping.GspVoElementMappingDeserializer;
import com.inspur.edp.formserver.viewmodel.json.mapping.GspVoElementMappingSerializer;
import com.inspur.edp.formserver.viewmodel.json.object.ViewObjectDeserializer;
import com.inspur.edp.formserver.viewmodel.json.object.ViewObjectSerializer;
import com.inspur.edp.formserver.viewmodel.util.ViewModelUtils;
import com.inspur.edp.formserver.vmmanager.exception.VmManagerException;
import com.inspur.edp.formserver.vmmanager.util.CheckInfoUtil;
import com.inspur.edp.formserver.vmmanager.validate.model.ViewModelChecker;
import io.iec.edp.caf.commons.exception.ExceptionLevel;
import lombok.var;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static com.inspur.edp.formserver.viewmodel.common.ConvertUtils.toElement;

public class WebControllerService {
  private String EXCEPTIONCODE = "WebControllerService";
  public static WebControllerService getInstance() {
    return new WebControllerService();
  }

  public String getBizObject(GspBusinessEntity be, String objid) {
    IGspCommonObject bebject = be.getAllObjectList().stream().filter((item)-> item.getID().equals(objid)).findFirst().orElse(null);

    ObjectMapper mapper=new ObjectMapper();
    SimpleModule module =new SimpleModule();
    module.addSerializer(IGspCommonDataType.class,new BizObjectSerializer());
    mapper.registerModule(module);
    try {

      String objJson =mapper.writeValueAsString(bebject);
      return  objJson;
    } catch (JsonProcessingException e) {
      throw new RuntimeException("GspBizEntityObject序列化失败"+e);
    }
  }

  /**
   * 检查关键字
   * @param jsonObject
   */
  public void checkKeywords(String jsonObject) {
    if (jsonObject == null || jsonObject.isEmpty()) {
      throw new RuntimeException("请传入有效Json对象.");
    }
    ObjectMapper mapper = new ObjectMapper();
    GspViewModel viewModel = null;
    try {
      viewModel = mapper.readValue(jsonObject, GspViewModel.class);
    } catch (JsonProcessingException e) {
      throw new RuntimeException(e);
    }
    ViewModelChecker.getInstance().check(viewModel);
  }

  /**
   * 获取虚拟vo关联
   * @param info
   * @return
   */
  public String getVirtualVoAsso(String info, EntityFunction func) {
    ObjectMapper mapper=new ObjectMapper();
    try {
      JsonNode node=mapper.readTree(info);
      String path = node.get("path").textValue();
      String refModelPkgName=null;
      if(!CheckInfoUtil.checkNull(node.get("refModelPkgName"))){
        refModelPkgName=node.get("refModelPkgName").textValue();
      }
      String refModelId = node.get("refModelId").textValue();
      String voAssoJson = node.get("voAsso").textValue();
      String refElementIdsJson = node.get("refElementIds").textValue();
      String voElementLabelId = node.get("voElementLabelId").textValue();

      String errorMessage=checkElementLabelId(voElementLabelId);
      if (ViewModelUtils.checkNull(errorMessage))
      {
        throw new VmManagerException("",EXCEPTIONCODE, errorMessage,null, ExceptionLevel.Error,false);
      }

      var originVoAsso = readAsso(voAssoJson, new ViewElementDeserializer());
      var refElementIds=readIdList(refElementIdsJson);

      if (refElementIds.size() == 0)
      {
        throw new VmManagerException("",EXCEPTIONCODE, "当前未选中关联带出字段。",null,ExceptionLevel.Error,false);
      }

      var originVoRefElements = originVoAsso.getRefElementCollection();
      ArrayList<IGspCommonElement> bizRefElements = func.getBizEntity(path,refModelPkgName,refModelId).getAllElementList(true);
      GspElementCollection refElements = new GspElementCollection(null);

      for (var refElementId : refElementIds)
      {
        IGspCommonElement voRefElement= (IGspCommonElement) originVoRefElements.stream().filter(item->item.getRefElementId().equals(refElementId)).findFirst().orElse(null);
        if (!(voRefElement instanceof GspViewModelElement))
        {
          IGspCommonElement beRefElement=bizRefElements.stream().filter(item-> item
              .getID().equals(refElementId)).findFirst().orElse(null);
          if (!(beRefElement instanceof GspBizEntityElement))
          {
            throw new VmManagerException("",EXCEPTIONCODE, "be中无Id='{"+refElementId+"}'的关联带出字段。",null,ExceptionLevel.Error,false);
          }
          // 虚拟字段的关联带出字段也是虚拟字段
          var newVoRefElement = getVirtualRefElement((GspBizEntityElement) beRefElement, voElementLabelId);
          refElements.add(newVoRefElement);
        }
        else
        {
          refElements.add(voRefElement);
        }
      }
      return writeViewElementsJson(refElements);
    } catch (JsonProcessingException e) {
      throw new RuntimeException("VirtualAssociation序列化失败"+e);
    }
  }


  private String checkElementLabelId(String voElementLabelId) {

    String message = "";
    if (ViewModelUtils.checkNull(voElementLabelId) || ViewModelUtils.checkNull(voElementLabelId.trim()))
    {
      message = "当前字段的 [标签] 属性不允许为空! ";

    }
    else
    {
      // LabelID在表单和报表中用作XML的标签，要限制非法字符
      String regex="\"^[A-Za-z_][A-Za-z_\\d-.]*$\"";
      Pattern pattern=Pattern.compile(regex);
      Matcher matcher=pattern.matcher(voElementLabelId.trim());
      boolean isMatch=matcher.matches();
      if (!isMatch)
      {
        message = "当前字段的 [标签] 属性\n必须以英文字母或下划线开头, 并且只能由英文字母、数字、下划线(_)、连线(-)和点(.)组成! ";
      }
    }
    return message;
  }

  public GspCommonAssociation readAsso(String beAssoJson, CmElementDeserializer cmElementDeserializer) {
    ObjectMapper mapper=new ObjectMapper();
    SimpleModule module=new SimpleModule();
    GspCommonAssociationDeserializer associationDeserializer=new GspCommonAssociationDeserializer(cmElementDeserializer);
    module.addDeserializer(GspAssociation.class,associationDeserializer);
    mapper.registerModule(module);
    try {
      GspCommonAssociation association= (GspCommonAssociation) mapper.readValue(beAssoJson,GspAssociation.class);
      return association;
    } catch (JsonProcessingException e) {
      throw new RuntimeException("GspCommonAssociation反序列化失败"+e);
    }
  }

  public List<String> readIdList(String beEleIds) {
    ObjectMapper mapper = new ObjectMapper();
    SimpleModule module=new SimpleModule();
    module.addDeserializer(String.class,new StringDeserializer());
    mapper.registerModule(module);
    JavaType type = mapper.getTypeFactory().
        constructCollectionType(List.class, String.class);
    List<String> ids=null;
    try {
      ids=mapper.readValue(beEleIds,type);
    } catch (JsonProcessingException e) {
      throw new RuntimeException("id列表反序列化失败"+e);
    }
    return ids;
  }

  public GspViewModelElement getVirtualRefElement(GspBizEntityElement beElement,String voElementLabelId)
  {
    GspViewModelElement ele = ConvertUtils.toElement(beElement, null, null, GspVoElementSourceType.BeElement);
    ele.setID(UUID.randomUUID().toString());
    ele.setIsVirtual(true);
    ele.setIsVirtualViewElement(true);
    ele.setMapping(null);
    ele.setLabelID(voElementLabelId+"_"+beElement.getLabelID());
    ele.setIsRefElement(true);
    ele.setRefElementId(beElement.getID());
    // 20200319-关联带出字段必填应为false
    // Bug 305714: 字段非必填，关联出的字段必填，生成表单时显示关联出的字段不应该为必填
    ele.setIsRequire(false);
    if (ele.getHasAssociation())
    {
      for (IGspCommonField item : ele.getChildAssociations().get(0).getRefElementCollection())
      {
        ((GspViewModelElement)item).setIsVirtual(true);
        ((GspViewModelElement)item).setIsVirtualViewElement(true);
        ((GspViewModelElement) item).setMapping(null);
      }
    }
    return ele;
  }

  public String writeViewElementsJson(GspElementCollection voElements) {
    ObjectMapper mapper=new ObjectMapper();
    SimpleModule module =new SimpleModule();
    module.addSerializer(IGspCommonField.class,new ViewElementSerializer());
    module.addDeserializer(IGspCommonField.class,new ViewElementDeserializer());
    mapper.registerModule(module);
    try {
      String vmJson=mapper.writeValueAsString(voElements);
      return  vmJson;
    } catch (JsonProcessingException e) {
      throw new RuntimeException("vo字段序列化异常"+e);
    }
  }

  public String convertBeElementIdsToVmElements(String convertEleInfo, EntityFunction func) {
    ObjectMapper mapper=new ObjectMapper();
    try {
      JsonNode node= mapper.readTree(convertEleInfo);
      String beEleIds = node.get("beEleIds").textValue();
      String bePkgName=null;
      if(!CheckInfoUtil.checkNull(node.get("bePkgName"))){
        bePkgName=node.get("bePkgName").textValue();
      }

      String beId = node.get("beId").textValue();
      String objId =node.get("objId").textValue();
      List<String> beEleIdList = readIdList(beEleIds);

      GspBusinessEntity be= func.getBizEntity(null, bePkgName, beId);
      GspBizEntityObject obj = (GspBizEntityObject) be.findObjectById(objId);
      GspElementCollection voElements = convertPartialElementsToVoElements(obj,beEleIdList, bePkgName,beId,
          GspVoElementSourceType.BeElement);
      return writeViewElementsJson(voElements);
    } catch (JsonProcessingException e) {
      throw new RuntimeException(e);
    }
  }

  public String addBizObject(String addObjInfo, EntityFunction func) {
    ObjectMapper mapper=getViewObjectMapper();
    try {
      JsonNode node= mapper.readTree(addObjInfo);
      String keysJson = node.get("keysJson").textValue();
      String beId = node.get("beId").textValue();
      String bePkgName=null;
      if(!CheckInfoUtil.checkNull(node.get("bePkgName"))){
        bePkgName=node.get("bePkgName").textValue();
      }
      String parentObjIDElementId = node.get("parentObjIDElementId").textValue();
      String bizObjCode = node.get("bizObjCode").textValue();


      // ①获取新增对象；
      GspBusinessEntity be = func.getBizEntity(null, null, beId);
      GspBizEntityObject bizObject = (GspBizEntityObject) be.findObjectByCode(bizObjCode);
      if (bizObject == null)
      {
        throw new VmManagerException("",EXCEPTIONCODE, "业务实体{"+be.getName()+"}中没有编号为{"+bizObjCode+"}的对象。",null,ExceptionLevel.Error,false);
      }
      bizObject.getKeys().clear();
      bizObject.getContainChildObjects().clear();
      var keys = readGspGspAssociationKeyCollection(keysJson);
      if (keys != null && keys.getCount()> 0)
      {
        for (GspAssociationKey key : keys)
        {
          bizObject.getKeys().add(key);
        }
      }

      // ②转换为voObject
      GspViewObject newViewObject = ConvertUtils.toObject(bizObject, bePkgName, beId, parentObjIDElementId,
          GspVoObjectSourceType.BeObject);

      return mapper.writeValueAsString(newViewObject);
    } catch (JsonProcessingException e) {
      throw new RuntimeException("GspAssociation序列化失败"+e);
    }
  }

  private ObjectMapper getViewObjectMapper(){
    ObjectMapper mapper=new ObjectMapper();
    SimpleModule module =new SimpleModule();
    module.addSerializer(IGspCommonDataType.class,new ViewObjectSerializer());
    module.addDeserializer(IGspCommonDataType.class,new ViewObjectDeserializer());
    mapper.registerModule(module);
    return  mapper;
  }

  //TODO 此处需要确认
  private GspAssociationKeyCollection readGspGspAssociationKeyCollection(String keysJson) {

    GspAssociationKeyCollection keys = new GspAssociationKeyCollection();

    ObjectMapper mapper=new ObjectMapper();
    SimpleModule module=new SimpleModule();
    module.addDeserializer(GspAssociationKey.class,new GspAssoKeyDeserializer());
    mapper.registerModule(module);
    try {
      JsonNode nodeList=mapper.readTree(keysJson);
      if(nodeList.getNodeType()== JsonNodeType.ARRAY)
        for(JsonNode item:nodeList){
          keys.add(mapper.readValue(item.toString(),GspAssociationKey.class));
        }
    } catch (JsonProcessingException e) {
      throw new RuntimeException(e);
    }

//    if (JToken.Parse(keysJson) is JArray jArray)
//    {
//      for (int i = 0; i < jArray.Count; i++)
//      {
//        JToken o = jArray[i];
//        GspAssociationKey con = SerializerUtils.ReadObject<GspAssociationKey>(o.CreateReader());
//        keys.Add(con);
//      }
//    }
    return keys;
  }

  private GspElementCollection convertPartialElementsToVoElements(IGspCommonObject co, List<String> eleIdList, String pkgName, String metaId, GspVoElementSourceType sourceType )
  {
    GspElementCollection voElements = new GspElementCollection(null);
    if (eleIdList != null && eleIdList.size() > 0)
    {
      for (String eleId : eleIdList)
      {
        GspBizEntityElement bizElement = (GspBizEntityElement) co.findElement(eleId);
        GspViewModelElement voElement = toElement(bizElement, pkgName, metaId, sourceType);
        voElement.getMapping().setTargetObjectId(co.getID());
        voElements.add(voElement);
      }
    }
    return voElements;
  }

  public  GspVoElementMapping readVoEleMapping(String voEleMappingJson) {
    ObjectMapper mapper=new ObjectMapper();
    SimpleModule module =new SimpleModule();
    module.addSerializer(ViewModelMapping.class,new GspVoElementMappingSerializer());
    module.addDeserializer(ViewModelMapping.class,new GspVoElementMappingDeserializer());
    mapper.registerModule(module);
    try {
      GspVoElementMapping voElemapping= (GspVoElementMapping) mapper.readValue(voEleMappingJson,ViewModelMapping.class);
      if(voElemapping==null){
        throw new VmManagerException("",EXCEPTIONCODE, "找不到当前vo字段映射的映射关系。",null,ExceptionLevel.Error,false);
      }
      return voElemapping;
    } catch (JsonProcessingException e) {
      throw  new RuntimeException("VoElementMapping反序列化失败"+e);
    }
  }

  public GspCommonAssociation getBizAsso(String path, GspVoElementMapping voEleMapping,
      String assoId, EntityFunction func) {
    GspBusinessEntity be = func.getBizEntity(path, voEleMapping.getTargetMetadataPkgName(), voEleMapping.getTargetMetadataId());
    String bizEleId = voEleMapping.getTargetElementId();
    GspBizEntityElement bizEle = (GspBizEntityElement) be.findElementById(bizEleId);
    if (bizEle == null)
    {
      throw new VmManagerException("",EXCEPTIONCODE, "找不到当前vo字段映射的be字段, 字段id={bizEleId}",null,ExceptionLevel.Error,false);
    }
    if (bizEle.getChildAssociations().getCount() == 0)
    {
      throw new VmManagerException("",EXCEPTIONCODE, "当前vo字段映射的be字段, 不是关联字段。",null,ExceptionLevel.Error,false);

    }
    GspAssociation association=  bizEle.getChildAssociations().stream().filter(item->item.getId().equals(assoId)).findFirst().orElse(null);
    if (!(association instanceof GspCommonAssociation))
    {
      throw new VmManagerException("",EXCEPTIONCODE, "找不到当前vo关联映射的be关联, 关联id={assoId}。",null,ExceptionLevel.Error,false);
    }
    return (GspCommonAssociation) association;
  }
}
